namespace SubSonic.CodeGenerator
{
    using SubSonic;
    using SubSonic.Utilities;
    using System;
    using System.CodeDom.Compiler;
    using System.Collections.Specialized;
    using System.Reflection;
    using System.Text;
    using System.Text.RegularExpressions;

    public class TurboCompiler
    {
        private CompilerParameters codeCompilerParameters;
        private CodeDomProvider codeProvider;
        internal StringBuilder errMsg = new StringBuilder();
        internal ICodeLanguage Language = new CSharpCodeLanguage();
        internal StringCollection References = new StringCollection();
        private static readonly Regex regLineFix = new Regex(@"[\r\n]+", RegexOptions.Compiled);
        private readonly TurboTemplateCollection templates = new TurboTemplateCollection();

        public void AddTemplate(TurboTemplate template)
        {
            if (this.templates.Count == 0)
            {
                this.References = template.References;
                this.Language = template.CompileLanguage;
            }
            template.EntryPoint = "Render";
            template.GeneratedRenderType = "Parser" + this.Templates.Count;
            template.TemplateText = Utility.FastReplace(template.TemplateText, "#TEMPLATENUMBER#", this.Templates.Count.ToString(), StringComparison.InvariantCultureIgnoreCase);
            this.templates.Add(template);
        }

        private void CallEntry(Assembly assembly)
        {
            int count = this.Templates.Count;
            Module module = assembly.GetModules(false)[0];
            for (int i = 0; i < count; i++)
            {
                Type type = module.GetType(this.Templates[i].GeneratedRenderType);
                if (type != null)
                {
                    MethodInfo method = type.GetMethod(this.Templates[i].EntryPoint, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
                    if (method != null)
                    {
                        StringBuilder builder = new StringBuilder();
                        string message = "Successfully generated " + this.Templates[i].TemplateName;
                        if (this.Templates[i].AddUsingBlock)
                        {
                            Utility.WriteTrace("Adding Custom Using Blocks");
                            builder.Append(this.Templates[i].RenderLanguage.DefaultUsingStatements);
                            builder.Append(this.Templates[i].CustomUsingBlock);
                        }
                        try
                        {
                            builder.Append((string) method.Invoke(null, null));
                        }
                        catch (Exception exception)
                        {
                            message = "An Exception occured in template " + this.Templates[i].TemplateName + ": " + exception.Message;
                        }
                        this.Templates[i].FinalCode = ScrubOutput(builder.ToString());
                        Utility.WriteTrace(message);
                    }
                }
            }
        }

        internal void ClearErrMsgs()
        {
            this.errMsg.Remove(0, this.errMsg.Length);
        }

        internal void LogErrMsgs(string customMsg)
        {
            this.LogErrMsgs(customMsg, null);
        }

        internal void LogErrMsgs(string customMsg, Exception ex)
        {
            this.errMsg.Append("\r\n").Append(customMsg).Append(Environment.NewLine);
            while (ex != null)
            {
                this.errMsg.Append("\t").Append(ex.Message).Append(Environment.NewLine);
                ex = ex.InnerException;
            }
        }

        public void Reset()
        {
            this.templates.Clear();
        }

        public void Run()
        {
            int count = this.Templates.Count;
            if (count > 0)
            {
                this.ClearErrMsgs();
                string[] sources = new string[count];
                for (int i = 0; i < count; i++)
                {
                    sources[i] = this.Templates[i].TemplateText;
                }
                Utility.WriteTrace("Compiling assembly...");
                CompilerResults results = this.CodeProvider.CompileAssemblyFromSource(this.CodeCompilerParameters, sources);
                Utility.WriteTrace("Done!");
                if ((results.Errors.Count > 0) || (results.CompiledAssembly == null))
                {
                    if (results.Errors.Count > 0)
                    {
                        foreach (CompilerError error in results.Errors)
                        {
                            this.LogErrMsgs("Compile Error: " + error.ErrorText);
                        }
                    }
                    if (results.CompiledAssembly == null)
                    {
                        string str = "Error generating template code: This usually indicates an error in template itself, such as use of reserved words. Detail: ";
                        Utility.WriteTrace(str + this.errMsg);
                        throw new Exception(str + Environment.NewLine + this.errMsg);
                    }
                }
                else
                {
                    Utility.WriteTrace("Extracting code from assembly and scrubbing output...");
                    this.CallEntry(results.CompiledAssembly);
                    Utility.WriteTrace("Done!");
                }
            }
        }

        private static string ScrubOutput(string result)
        {
            if (!string.IsNullOrEmpty(result))
            {
                result = regLineFix.Replace(result, "\r\n");
                result = Utility.FastReplace(result, "}", "}\r\n", StringComparison.InvariantCultureIgnoreCase);
                result = Utility.FastReplace(result, "namespace", "\r\nnamespace", StringComparison.InvariantCulture);
                result = Utility.FastReplace(result, "public class ", "\r\npublic class ", StringComparison.InvariantCulture);
                result = Utility.FastReplace(result, "Public Class ", "\r\nPublic Class ", StringComparison.InvariantCulture);
                result = Utility.FastReplace(result, "[<]", "<", StringComparison.InvariantCultureIgnoreCase);
                result = Utility.FastReplace(result, "[>]", ">", StringComparison.InvariantCultureIgnoreCase);
            }
            return result;
        }

        private CompilerParameters CodeCompilerParameters
        {
            get
            {
                if (this.codeCompilerParameters == null)
                {
                    this.codeCompilerParameters = new CompilerParameters();
                    this.codeCompilerParameters.CompilerOptions = "/target:library";
                    this.codeCompilerParameters.GenerateExecutable = false;
                    this.codeCompilerParameters.GenerateInMemory = true;
                    this.codeCompilerParameters.IncludeDebugInformation = false;
                    this.codeCompilerParameters.ReferencedAssemblies.Add("mscorlib.dll");
                    foreach (string str in this.References)
                    {
                        this.codeCompilerParameters.ReferencedAssemblies.Add(str);
                    }
                }
                return this.codeCompilerParameters;
            }
        }

        private CodeDomProvider CodeProvider
        {
            get
            {
                if (this.codeProvider == null)
                {
                    this.codeProvider = this.Language.CreateCodeProvider();
                }
                return this.codeProvider;
            }
        }

        public TurboTemplateCollection Templates
        {
            get
            {
                return this.templates;
            }
        }
    }
}

